
#include "porting.h"
#include "lnthread.h"
#include "coumo_errorcode.h"

#include <memory.h>
#include <stdarg.h>
#include <stdio.h>

#ifdef WINDOWS

__declspec(thread)
struct ln_thread_t thrd_current_block;

LN_THREAD_PTR thrd_current ()
  {
    return &thrd_current_block;
  }

#else

#endif

void     thrd_init        (LN_THREAD_PTR thrd, const char* thrd_name)
{
  thrd_verify (thrd);

  memset (thrd, 0, sizeof (*thrd) );
  thrd->thrd_name = thrd_name;

#ifdef WINDOWS
  thrd->heap = HeapCreate (0,0,0);
  thrd->tid  = GetCurrentProcessId();
#endif

};

void     thrd_close       (LN_THREAD_PTR thrd)
{
  thrd_verify (thrd);

#ifdef WINDOWS
  HeapDestroy ( thrd->heap );
#endif
}


void*    thrd_alloc       (LN_THREAD_PTR thrd, int   size)
{
  void* r;

#ifdef WINDOWS
  if ( thrd)
  {
    r = HeapAlloc ( thrd->heap, 0, size);
  }
  else
  {
    r = HeapAlloc ( GetProcessHeap(), 0, size);
  }
#else
  if ( thrd)
  {
    /* todo */
  }
  else
  {
    r = (char*)malloc (size);
  }
#endif

  if (r == NULL)
  {
    er_set (thrd, LN_ER_OUT_OF_MEMORY, size);
  }

  return r;
};

void     thrd_free        (LN_THREAD_PTR thrd, void* p)
{
#ifdef WINDOWS
  if (thrd)
  {
    HeapFree ( thrd->heap, 0, p);
  }
  else
  {
    HeapFree ( GetProcessHeap(), 0, p );
  }
#else
  if (thrd)
  {
    /* todo */
  }
  else
  {
    free (thrd);
  }
#endif
};


char*   respool_alloc (respool * pool, int size)
{
  char * r;
  int new_size;

  size +=  ( sizeof(void*) - 1);
  size &= ~( sizeof(void*) - 1);

  if (pool->buff_size >= size)
  {
    r = pool->buff;
    pool->buff += size;
    pool->buff_size -= size;

    return r;
  }

  new_size = size > 4000 ? size + sizeof (void*) : 4096;

  r = (char*) thrd_alloc (pool->thrd, new_size );

  if (r == NULL)
  {
    return r;
  };

  *((char**)r) = pool->pool;
  pool->pool = r;

  r += sizeof(void*);

  if (size < 4000)
  {
    pool->buff      = r + size;
    pool->buff_size = 4096 - sizeof(void*) - size;
  };

  pool->total_size += new_size;
  return r;
};

void    respool_close (respool * pool)
{
  thrd_verify (pool->thrd);

  while ( pool->pool )
  {
    char* next = *( (char**)(pool->pool) );
    thrd_free (pool->thrd, pool->pool);
    pool->pool = next;
  };
}


string_map_p er_msg_map_global = (void*)0;

/*
  function er_set_message
  msg[in] : the error message string list;
*/
void er_set_message (string_map_p msg)
{
  er_msg_map_global = msg;
};

/*
  function er_set
  thrd[in]  : 
  level[in] : 
  id[in]    : error id
*/

void er_set (LN_THREAD_PTR thrd, int id, ...)
{
  va_list va;
  string_map_p ermsg;

  va_start (va, id);

  if (thrd == (void*)0 )
  {
    thrd = thrd_current ();
  };

  if (er_msg_map_global)
  {
    ermsg = string_map_at (
              er_msg_map_global,
              thrd->err_cache,
              COUNT_OF(thrd->err_cache),
              id);
  }
  else
  {
    ermsg = NULL;
  }

  if (ermsg)
  {
    p_vsnprintf (thrd->err_msg, sizeof (thrd->err_msg), ermsg->string, va );
  }
  else
  {
    snprintf (thrd->err_msg, sizeof (thrd->err_msg), "Error code %d", id);
  };

  thrd->err_id = id;
};
